﻿using Abp.Application.Services.Dto;
using Abp.Authorization;
using Abp.AutoMapper;
using Abp.Domain.Repositories;
using Abp.Domain.Uow;
using Abp.Linq.Extensions;
using Abp.UI;
using Hicap.AuthorizationServer.CommonDto;
using Hicap.AuthorizationServer.Core.Caches.Sys;
using Hicap.AuthorizationServer.Core.Caches.Sys.Interface;
using Hicap.AuthorizationServer.Core.Common;
using Hicap.AuthorizationServer.Core.Domains.Sys.Users;
using Hicap.AuthorizationServer.Core.Models.Sys;
using Hicap.AuthorizationServer.Sys.AppService.Interface;
using Hicap.AuthorizationServer.Sys.DTO.Roles;
using Hicap.AuthorizationServer.Sys.DTO.Users;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Dynamic.Core;
using System.Linq.Expressions;
using System.Threading.Tasks;

namespace Hicap.AuthorizationServer.Sys.AppService
{
    /// <summary>
    /// User应用层服务的接口实现方法
    /// </summary>
    public class UserAppService
        : AuthorizationServerAppServiceBase<User, UserAppService, UserEditDto, UserListDto, UserCacheItem>, IUserAppService
    {
        ////BCC/ BEGIN CUSTOM CODE SECTION
        ////ECC/ END CUSTOM CODE SECTION
        private readonly IRepository<UserRoleRelated> _userRoleRelatedRepository;

        private readonly IRepository<User, int> _userRepository;
        private readonly IRepository<Company, int> _companyRepository;
        private readonly IUsersManager _usersManager;

        private readonly IUnitOfWorkManager _unitOfWorkManager;

        /// <summary>
        /// 构造函数
        /// </summary>
        public UserAppService(IRepository<User, int> userRepository,
            IUsersManager usersManager,
            IUnitOfWorkManager unitOfWorkManager,
            IRepository<User> repository,
            IRepository<UserRoleRelated> userRoleRelatedRepository,
            IUserCache cache, IRepository<Company, int> companyRepository)
            : base(
                repository,
                unitOfWorkManager,
                cache)
        {
            _unitOfWorkManager = unitOfWorkManager;
            _usersManager = usersManager;
            _userRoleRelatedRepository = userRoleRelatedRepository;
            _companyRepository = companyRepository;
            _userRepository = userRepository;

            ForeignKeys.Add(x => x.SubordinateCompany);

            CreateActions.Add(async (input, entity) => await CreatePassword(input, entity));
            UpdateActions.Add(async (input, entity) => await LinkCompanyIdAsync(input, entity));

            UpdateActions.Add(async (input, entity) => await UpdatePassword(input, entity));
            CreateActions.Add(async (input, entity) => await LinkCompanyAsync(input, entity));
        }

        private Task LinkCompanyIdAsync(UserEditDto input, User entity)
        {
            return Task.Run(() =>
            {
                if (input.SubordinateCompanyId.HasValue) entity.SubordinateCompany.Id = input.SubordinateCompanyId.Value;
            });
        }

        private async Task LinkCompanyAsync(UserEditDto input, User entity)
        {
            if (input.SubordinateCompanyId.HasValue)
            {
                entity.SubordinateCompany = await _companyRepository.GetAsync(input.SubordinateCompanyId.Value);
            }
        }

        [HicapAuthorize(SpeicaAuthCode.UserUpdateSelf)]
        public async Task<UserListDto> UpdateSelfUserInfo(UserEditDto input)
        {
            if (AbpSession.UserId.HasValue && input.Id.HasValue && AbpSession.UserId.Value.Equals(input.Id.Value))
            {
                input.Password = string.Empty;
                return await base.Update(input);
            }
            throw new AbpAuthorizationException();
        }

        [AllowAnonymous]
        public async Task<UserListDto> Register(UserEditDto input)
        {
            input.IsActive = false;
            UserListDto result = await base.Create(input);

            if (result != null)
            {
                await _usersManager.NotificationUserActive(result.MapTo<UserListDto>());
            }

            return result;
        }

        [HicapAuthorize(SpeicaAuthCode.UserActive)]
        public async Task<UserListDto> ActiveUser([FromBody]int userId)
        {
            User user = await _usersManager.ActiveUser(userId);
            return user.MapTo<UserListDto>();
        }

        [AllowAnonymous]
        public async Task ForgetPassword(UserEditDto input)
        {
            User user = await _usersManager.FindByUserCode(input.UserCode);
            UserListDto data = user.MapTo<UserListDto>();
            await _usersManager.NotificationUserForgetPassword(data);
        }

        /// <summary>
        /// 获取User的分页列表信息
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public override async Task<PagedResultDto<UserListDto>> PagedDatas(QueryInput input)
        {
            IQueryable<User> query = GetQuery(input);

            int userCount = await query.CountAsync();

            List<User> users = await query
               .OrderBy(input.Sorting)
               .PageBy(input)
               .ToListAsync();

            List<UserListDto> userListDtos = users.MapTo<List<UserListDto>>();

            foreach (UserListDto userListDto in userListDtos)
            {
                List<Expression<Func<UserRoleRelated, dynamic>>> keys = new List<Expression<Func<UserRoleRelated, dynamic>>>()
                {
                    x => x.User,
                    x => x.Role
                };
                foreach (UserRoleRelated userRoleRelated in _userRoleRelatedRepository
                    .GetAllIncluding(keys.ToArray()).Where(x => x.User.Id == userListDto.Id))
                {
                    Role role = userRoleRelated.Role;
                    RoleListDto map = role.MapTo<RoleListDto>();
                    map.UserRoleReleateId = userRoleRelated.Id;
                    userListDto.Roles.Add(map);
                }
            }

            return new PagedResultDto<UserListDto>(
                userCount,
                userListDtos
                );
        }

        private Task CreatePassword(UserEditDto input, User dbEntity)
        {
            return Task.Run(() =>
            {
                if (!string.IsNullOrEmpty(input.Password))
                    dbEntity.CurPassword = _usersManager.GetPasswordSha256(input.Password);
            });
        }

        private Task UpdatePassword(UserEditDto input, User dbEntity)
        {
            return UpdatePassword(input.Password, dbEntity);
        }

        private Task UpdatePassword(UserPasswordEditDTO input, User dbEntity)
        {
            return UpdatePassword(input.NewPassword, dbEntity);
        }

        private Task UpdatePassword(string password, User dbEntity)
        {
            return Task.Run(() =>
            {
                if (!string.IsNullOrEmpty(password))
                {
                    dbEntity.BeforeLastPassword = dbEntity.LastPassword;
                    dbEntity.LastPassword = dbEntity.CurPassword;
                    dbEntity.CurPassword = _usersManager.GetPasswordSha256(password);
                }
            });
        }

        /// <summary>
        /// 管理员修改密码
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public Task ResetPasswordForAdmin(UserPasswordEditDTO input)
        {
            //            PermissionCheck("UpsertAdmin");
            return Task.Run(async () =>
            {
                if (!input.Id.HasValue)
                    throw new UserFriendlyException(L("NoUserCanBeChangePassword"));

                if (!string.IsNullOrEmpty(input.NewPassword))
                {
                    User dbEntity = _userRepository.Get(input.Id.Value);
                    //                    await UpdatePassword(input, dbEntity);
                    dbEntity.CurPassword = _usersManager.GetPasswordSha256(input.NewPassword);
                    await _userRepository.UpdateAsync(dbEntity);
                }
            });
        }

        /// <summary>
        /// 普通用户修改密码
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public Task ResetPassword(UserPasswordEditDTO input)
        {
            return Task.Run(async () =>
            {
                if (!input.Id.HasValue && AbpSession.UserId.HasValue)
                    input.Id = Convert.ToInt32(AbpSession.UserId.Value);

                if (!string.IsNullOrEmpty(input.CurrentPassword) && !string.IsNullOrEmpty(input.NewPassword) && input.Id.HasValue)
                {
                    User dbEntity = _userRepository.Get(input.Id.Value);

                    string currentPasswordSha256 = _usersManager.GetPasswordSha256(input.CurrentPassword);

                    string newPasswordSha256 = _usersManager.GetPasswordSha256(input.NewPassword);

                    //旧密码验证
                    if (!dbEntity.CurPassword.Equals(currentPasswordSha256))
                        throw new UserFriendlyException(L("OriginalPasswordError"));

                    //近三次密码不能相同
                    if (dbEntity.CurPassword.Equals(newPasswordSha256))
                        throw new UserFriendlyException(L("NearlyThreePasswordsCannotBeTheSame"));

                    if (!string.IsNullOrEmpty(dbEntity.LastPassword) && dbEntity.LastPassword.Equals(newPasswordSha256))
                        throw new UserFriendlyException(L("NearlyThreePasswordsCannotBeTheSame"));

                    if (!string.IsNullOrEmpty(dbEntity.BeforeLastPassword) && dbEntity.BeforeLastPassword.Equals(newPasswordSha256))
                        throw new UserFriendlyException(L("NearlyThreePasswordsCannotBeTheSame"));

                    await UpdatePassword(input, dbEntity);
                    await _userRepository.UpdateAsync(dbEntity);
                }
            });
        }
    }
}